git clone https://github.com/google/googletest.git
cd googletest
mkdir build && cd build
cmake ..
sudo make install
代码就编译完成
然后将build中的lib库拷贝到跟googletest文件夹所在的同一个目录下,也是我们写代码的主要目录(001---C++)
(base) shengtangtang@MacBook-Pro build % cp -R lib ../../
再找到include头文件,拷贝里面的文件到跟lib同样的目录下
(base) shengtangtang@MacBook-Pro googletest % cd googletest
(base) shengtangtang@MacBook-Pro googletest % cp -R include/ ../../
然后在当前目录下新建include文件夹存放头文件
(base) shengtangtang@MacBook-Pro 001---C++ % mkdir include
(base) shengtangtang@MacBook-Pro 001---C++ % mv gtest include
(base) shengtangtang@MacBook-Pro 001---C++ % cd include
(base) shengtangtang@MacBook-Pro include % ls
gtest
至此,使用googletest的前期准备就做好了
编写测试程序 demo.cpp
(base) shengtangtang@MacBook-Pro 001---C++ % vim demo.cpp
#include <stdio.h>
#include <gtest/gtest.h>
using namespace std;
int add(int a, int b) {
return a + b;
}
TEST(test, add1) {
EXPECT_EQ(add(3, 4), 7); // ==
EXPECT_NE(add(3, 4), 6); // !=
EXPECT_LT(add(3, 4), 8); // <
EXPECT_LE(add(3, 4), 7); // <=
EXPECT_GT(add(3, 4), 6); // >
EXPECT_GE(add(3, 4), 7); // >=
}
TEST(test, add2) {
EXPECT_EQ(add(3, 4), 7); // ==
EXPECT_NE(add(3, 4), 6); // !=
EXPECT_LT(add(3, 4), 6); // <
EXPECT_LE(add(3, 4), 7); // <=
EXPECT_GT(add(3, 4), 6); // >
EXPECT_GE(add(3, 4), 7); // >=
}
int main() {
return RUN_ALL_TESTS();
}
为了方便编译,写一个 makefile 文件
(base) shengtangtang@MacBook-Pro 001---C++ % vim makefile
然后 make 一下, 生成 a.out文件
(base) shengtangtang@MacBook-Pro 001---C++ % make
g++ --std=c++11 -I./include -L./lib demo.cpp -lgtest
运行 a.out
在当前目录下创建我们的工程mygooletest,在里面新建 include文件夹,在里面再新建 ktest.h 头文件
封装颜色时,将 color.cpp 文件放在 我们的工程里, 然后
#include <stdio.h>
#include <shengtang/ktest.h>
int main() {
printf("hello world\n");
printf(RED("hello world\n"));
printf(GREEN("hello world\n"));
printf(YELLOW("hello world\n"));
printf(BLUE("hello world\n"));
return 0;
}
(base) shengtangtang@MacBook-Pro mygooletest % g++ -I./include color.cpp
(base) shengtangtang@MacBook-Pro mygooletest % ./a.out
#ifndef _KTEST_H
#define _KTEST_H
#define LOG(frm, args...) { \
printf("[%s : %s : %d]", __FILE__, __func__ ,__LINE__); \
printf(frm, ##args); \
printf("\n"); \
}
// 颜色封装
#define COLOR(msg, code) "\033[0;" #code "m" msg "\033[0m"
#define RED(msg) COLOR(msg, 31)
#define GREEN(msg) COLOR(msg, 32)
#define YELLOW(msg) COLOR(msg, 33)
#define BLUE(msg) COLOR(msg, 34)
#define EXPECT(a, comp, b) { \
__typeof(a) __a = (a), __b = (b); \
if (!((__a) comp (__b))) { \
func_flag = 0; \
printf(YELLOW(" %s : %d : Failure\n"), __FILE__, __LINE__); \
printf(YELLOW(" Expected: (%s) %s (%s), actual: %d vs %d\n"), \
#a, #comp, #b, __a, __b); \
} \
}
#define EXPECT_EQ(a, b) EXPECT(a, ==, b);
#define EXPECT_NE(a, b) EXPECT(a, !=, b);
#define EXPECT_LT(a, b) EXPECT(a, <, b);
#define EXPECT_LE(a, b) EXPECT(a, <=, b);
#define EXPECT_GT(a, b) EXPECT(a, >, b);
#define EXPECT_GE(a, b) EXPECT(a, >=, b);
//扩展出来的函数头部信息
// //扩展出来相关的注册函数
#define FUNC_NAME(a, b) shengtang_##a##_##b
#define TEST(a, b) \
void FUNC_NAME(a, b)(); \
__attribute__((constructor)) \
void reg_##shengtang_##a##_##b() { \
add_test_function(FUNC_NAME(a, b), #a "." #b); \
} \
void FUNC_NAME(a, b)()
// 存储区, 每个位置存储一个测试用例的函数地址
struct FuncData {
void (*func)(); //测试用例的函数地址
const char *func_name; // 测试用例的函数名称
} func_arr[100];
int fun_arr_cnt = 0; //计数量,计数当前存储区中存储了多少个测试用例
int func_flag;
void add_test_function(void (*func)(), const char *str) {
func_arr[fun_arr_cnt].func = func;
func_arr[fun_arr_cnt].func_name = str;
fun_arr_cnt += 1;
return ;
}
const char *RUN = GREEN("[ RUN ]");
const char *OK = GREEN("[ OK ]");
const char *FAILED = RED("[ FAILED ]");
int RUN_ALL_TESTS() {
// 遍历运行每个测试用例
for (int i = 0; i < fun_arr_cnt; ++i) {
printf("%s %s\n", RUN, func_arr[i].func_name);
func_flag = 1;
long long b = clock();
func_arr[i].func();
long long e = clock();
if (func_flag)
printf("%s", OK);
else
printf("%s ", FAILED);
printf("%s "YELLOW("(%lld ms)") "\n", func_arr[i].func_name,
(e - b) * 1000 / CLOCKS_PER_SEC);
}
return 0;
}
#endif
然后新建 makefile 文件,里面写入编译规则
运行结果:
/include/gtest.h
扩展的宏 GTEST_TEST_ 定义在 /internal/gtest-internal.h中
最后一行扩展的也是函数的头部信息, TEST中封装成了相关的类,
1549行的 MakeAndRegisterTestInfo 就是注册函数的功能,注册测试用例的信息
用自己实现的测试框架对快速排序算法进行测试
#include <iostream>
#include <vector>
#include <time.h>
#include <shengtang/ktest.h>
using namespace std;
void quick_sort_v1(int *arr, int l, int r) {
if (l >= r)
return;
int x = l, y = r, z = arr[l];
while (x < y) {
while (x < y && arr[y] >= z)
--y;
if (x < y)
arr[x++] = arr[y];
while (x < y && arr[x] <= z)
++x;
if (x < y)
arr[y--] = arr[x];
}
arr[x] = z;
quick_sort_v1(arr, l, x - 1);
quick_sort_v1(arr, x + 1, r);
return ;
}
#define MAX_N 10000000
int *arr;
int *getRandData(int n) {
int *arr = (int *)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
arr[i] = rand() % n;
}
return arr;
}
int check(int *arr, int n) {
for (int i = 1; i < n; ++i) {
if (arr[i] < arr[i - 1])
return 0;
}
return 1;
}
int *copyArray(int *arr, int n) {
int *tmp = (int *)malloc(sizeof(int) * n);
memcpy(tmp,arr, sizeof(int) * n);
return tmp;
}
TEST(test, quick_sort_v1) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v1(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
int main() {
srand(time(0));
arr = getRandData(MAX_N);
return RUN_ALL_TESTS();
}
在 makefile 文件中 加入对此文件的编译
然后运行,可以看出结果正确
C++ STL的Sort排序,面对大数据时,使用快速排序,对大数据进行分区。分为多个组后,组间有序,组内无序,然后在每组内对小规模数据进行插入排序的时间复杂度可达到O(N),
在 Sort中设置了一个递归深度控制,当当前快速排序的层数过深后,直接改成堆排序,
C++ STL sort 优化的地方:
#include <iostream>
#include <vector>
#include <time.h>
#include <shengtang/ktest.h>
using namespace std;
void quick_sort_v1(int *arr, int l, int r) {
if (l >= r)
return;
int x = l, y = r, z = arr[l];
while (x < y) {
while (x < y && arr[y] >= z)
--y;
if (x < y)
arr[x++] = arr[y];
while (x < y && arr[x] <= z)
++x;
if (x < y)
arr[y--] = arr[x];
}
arr[x] = z;
quick_sort_v1(arr, l, x - 1);
quick_sort_v1(arr, x + 1, r);
return ;
}
#define swap(a, b) { \
__typeof(a) __a = a; \
a = b, b = __a; \
}
// 三点取中法
int median(int a, int b, int c) {
if (a > b)
swap(a, b)
if (a > c)
swap(a, c);
if (b > c)
swap(b, c);
return b;
}
// 单边递归法,三点取中法, 无监督partition
void quick_sort_v2(int *arr, int l, int r) {
while (l < r) {
int x = l, y = r;
int z = median(arr[l], arr[(l + r) >> 1], arr[r]); //三点取中
do {
while (arr[x] < z)
x++;
while (arr[y] > z)
y--;
if (x <= y) {
swap(arr[x], arr[y]);
x++;
y--;
}
}while (x <= y);
quick_sort_v2(arr, x, r); //对右区间进行递归排序,
r = y; // 左区间在本层进行排序
}
return ;
}
#define MAX_N 10000000
int *arr;
int *getRandData(int n) {
int *arr = (int *)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
arr[i] = rand() % n;
}
return arr;
}
int check(int *arr, int n) {
for (int i = 1; i < n; ++i) {
if (arr[i] < arr[i - 1])
return 0;
}
return 1;
}
int *copyArray(int *arr, int n) {
int *tmp = (int *)malloc(sizeof(int) * n);
memcpy(tmp,arr, sizeof(int) * n);
return tmp;
}
TEST(test, quick_sort_v1) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v1(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
TEST(test, quick_sort_v2) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v2(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
int main() {
srand(time(0));
arr = getRandData(MAX_N);
return RUN_ALL_TESTS();
}
优化过后的快速排序性能比较
最终版本的快速排序
#include <iostream>
#include <vector>
#include <time.h>
#include <shengtang/ktest.h>
using namespace std;
// 版本1, 简单的快速排序
void quick_sort_v1(int *arr, int l, int r) {
if (l >= r)
return;
int x = l, y = r, z = arr[l];
while (x < y) {
while (x < y && arr[y] >= z)
--y;
if (x < y)
arr[x++] = arr[y];
while (x < y && arr[x] <= z)
++x;
if (x < y)
arr[y--] = arr[x];
}
arr[x] = z;
quick_sort_v1(arr, l, x - 1);
quick_sort_v1(arr, x + 1, r);
return ;
}
#define swap(a, b) { \
__typeof(a) __a = a; \
a = b, b = __a; \
}
// 三点取中法
int median(int a, int b, int c) {
if (a > b)
swap(a, b)
if (a > c)
swap(a, c);
if (b > c)
swap(b, c);
return b;
}
// 版本2,单边递归法,三点取中法, 无监督partition
void quick_sort_v2(int *arr, int l, int r) {
while (l < r) {
int x = l, y = r;
int z = median(arr[l], arr[(l + r) >> 1], arr[r]); //三点取中
do {
while (arr[x] < z)
x++;
while (arr[y] > z)
y--;
if (x <= y) {
swap(arr[x], arr[y]);
x++;
y--;
}
}while (x <= y);
quick_sort_v2(arr, x, r); //对右区间进行递归排序,
r = y; // 左区间在本层进行排序
}
return ;
}
const int threshold = 16;
void __quick_sort_v3(int *arr, int l, int r) {
while (r - l > threshold) {
int x = l, y = r;
int z = median(arr[l], arr[(l + r) >> 1], arr[r]); //三点取中
do {
while (arr[x] < z)
x++;
while (arr[y] > z)
y--;
if (x <= y) {
swap(arr[x], arr[y]);
x++;
y--;
}
}while (x <= y);
__quick_sort_v3(arr, x, r); //对右区间进行递归排序,
r = y; // 左区间在本层进行排序
}
return ;
}
// 插入排序,优化版
void insert_sort(int *arr, int l, int r) {
int ind = l;
for (int i = l + 1; i <= r; ++i) {
if (arr[ind] > arr[i])
ind = i;
}
swap(arr[ind], arr[l]);
for (int i = l + 2; i <= r; ++i) {
int j = i;
while (arr[j] < arr[j - 1]) {
swap(arr[j], arr[j - 1]);
--j;
}
}
return ;
}
//版本3, 设定阈值 + 插入排序
void quick_sort_v3(int *arr, int l, int r) {
__quick_sort_v3(arr, l, r);
insert_sort(arr, l, r);
return ;
}
#define MAX_N 10000000
int *arr;
int *getRandData(int n) {
int *arr = (int *)malloc(sizeof(int) * n);
for (int i = 0; i < n; i++) {
arr[i] = rand() % n;
}
return arr;
}
int check(int *arr, int n) {
for (int i = 1; i < n; ++i) {
if (arr[i] < arr[i - 1])
return 0;
}
return 1;
}
int *copyArray(int *arr, int n) {
int *tmp = (int *)malloc(sizeof(int) * n);
memcpy(tmp,arr, sizeof(int) * n);
return tmp;
}
TEST(test, quick_sort_v1) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v1(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
TEST(test, quick_sort_v2) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v2(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
TEST(test, quick_sort_v3) {
int *tmp = copyArray(arr, MAX_N);
quick_sort_v3(tmp, 0, MAX_N - 1);
EXPECT_EQ(check(tmp, MAX_N), 1);
}
int main() {
srand(time(0));
arr = getRandData(MAX_N);
return RUN_ALL_TESTS();
}
什么是好的测试框架:
例:运行sample1_unittest.cc
定位到这些文件所在的目录下:
(base) shengtangtang@MacBook-Pro samples % g++ sample1.cc sample1_unittest.cc -o sample1 -lpthread -lgtest -std=c++11 -lgtest_main
因为没有 main函数,所有后面加 -lgtest_main,然后运行。
也可以在 sample1_unittest.cc 后面加上main函数,运行
int main() {
return RUN_ALL_TESTS();
}
从 2个测试套件中运行6个测试组建,
Global test environment set-up: 初始化环境
Global test environment tear-down: 销毁环境
GTest框架将我们相同测试用例名的场景合并在一起,不同测试特例名的场景分开展现。而且我们还发现GTest有自动统计结果、自动格式化输出结果、自动调度执行等特性
虽然上例中,所有的执行都是正确的,但是如果以上测试中发生一个错误,也不能影响其他测试——不同测试用例不相互影响、相同测试用例不同测试特例不相互影响。我们称之为独立性。除了独立性,也不失灵活性——一个测试测试特例中可以通过不同宏(ASSERT_*类宏会影响之后执行,EXPECT_*类宏不会)控制是否影响之后的执行
如果我们编写的测试用例组(如上例是两组)中一组发生了错误,我们希望没出错的那组不用执行了,出错的那组再执行一遍。一般情况下,我们可能需要去删除执行正确的那段测试代码,但是这种方式非常不优美——需要编译或者忘记恢复代码。GTest框架可以让我们通过在程序参数控制执行哪个测试用例,比如我们希望只执行Factorial测试,就可以这样调用程序
./sample1_unittest --gtest_filter=Factorial*
我们可以将以上特性称之为 选择性测试
若要测试很多参数的话,则要写多次 EXPECT_TRUE,因此使用参数化,在后面添加此代码:
告诉gtest你的参数类型是什么
你必须添加一个类,继承testing::TestWithParam<T>,其中T就是你需要参数化的参数类型
告诉gtest你拿到参数的值后,具体做些什么样的测试
这里,我们要使用一个新的宏(嗯,挺兴奋的):TEST_P,关于这个"P"的含义,Google给出的答案非常幽默,就是说你可以理解为”parameterized" 或者 "pattern"。我更倾向于 ”parameterized"的解释,呵呵。在TEST_P宏里,使用GetParam()获取当前的参数的具体值
告诉gtest你想要测试的参数范围是什么
使用INSTANTIATE_TEST_CASE_P这宏来告诉gtest你要测试的参数范围
第一个参数是测试案例的前缀,可以任意取
第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:IsPrimeParamTest
第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数。Google提供了一系列的参数生成的函数:
说明:这个功能只在提供了<tr1/tuple>头的系统中有效。gtest会自动去判断是否支持tr/tuple,如果你的系统确实支持,而gtest判断错误的话,你可以重新定义宏GTEST_HAS_TR1_TUPLE=1
// 参数化
/* 1. 添加类,继承自 public::testing::TestWithParam<T>*/
class IsPrimeParamTest : public::testing::TestWithParam<int>
{
};
/* 2. 获取参数,并测试 */
TEST_P(IsPrimeParamTest, HandleTrueReturn)
{
int n = GetParam();
EXPECT_TRUE(IsPrime(n));
}
/* 3. 告诉 gtest 你的测试参数 */
INSTANTIATE_TEST_SUITE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
// 通过枚举将这5个数同时作为参数传入 IsPrime(n), googletest 就可以进行相同类型多用例测试
编译运行后:
在GTest中有了测试事件的这个机制,就能能够在测试之前或之后能够做一些准备/清理的操作。根据事件执行的位置不同,我们可将测试事件分为三种:
这个级别的事件会在TestCase之前与之后执行, 每个测试用例执行前后,里面的对象在每个测试用例的前面和后面(调用之初和调用结束时)调用相应的函数如 SetUp() 和 TearDown(),多次对类初始使用测试
类中的变量在一个测试用例中是存在的,
TestCase测试事件,需要实现两个函数SetUp()和TearDown()。
这两个函数是不是有点像类的构造函数和析构函数,但是切记他们并不是构造函数和析构函数。这两个函数是testing::Test的成员函数,我们在编写测试类时需要继承testing::Test。
#include <iostream>
#include <gtest/gtest.h>
class calcFunction
{
public:
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
};
class calcFunctionTest : public testing::Test
{
protected:
virtual void SetUp()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
virtual void TearDown()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
calcFunction calc;
};
TEST_F(calcFunctionTest, test_add)
{
std::cout << "--> test_add start <--" << std::endl;
EXPECT_EQ(calc.add(1,2), 3);
std::cout << "--> test_add end <--" << std::endl;
}
TEST_F(calcFunctionTest, test_sub)
{
std::cout << "--> test_sub start <--" << std::endl;
EXPECT_EQ(calc.sub(1,2), -1);
std::cout << "--> test_sub end <--" << std::endl;
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
这个级别的事件会在TestSuite中第一个TestCase之前与最后一个TestCase之后执行, 在某一批案例中,第一个执行前到最后一个执行后,类只会调用一次,
一般用于类行为测试,或有其他联系到多个方法测试
类中的值在类的生命周期中一直存在
TestSuite测试事件,同样的也需要实现的两个函数SetUpTestCase()和TearDownTestCase(),而这两个函数是静态函数。这两个静态函数同样也是testing::Test类的成员,我们直接改写下测试类calcFunctionTest,添加两个静态函数SetUpTestCase()和TearDownTestCase()到测试类中即可
class calcFunctionTest : public testing::Test
{
protected:
static void SetUpTestCase()
{
std::cout<< "--> " << __func__ << " <--" << std::endl;
}
static void TearDownTestCase()
{
std::cout<< "--> " << __func__ << " <--" << std::endl;
}
virtual void SetUp()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
virtual void TearDown()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
calcFunction calc;
};
改写好之后,我们再看一下运行结果。这两个函数分别是在本TestSuite中的第一个TestCase之前和最后一个TestCase之后执行
这些级别的事件会在所有TestCase中第一个执行前,与最后一个之后执行,可用于组合类行为测试,类中的成员变量是全局变量,在return结束之后才被清理
全局测试事件,也需要继承一个类,但是它需要继承testing::Environment类实现SetUp()和TearDown()两个函数。还需要在main函数中调用testing::AddGlobalTestEnvironment方法注册全局事件
#include <iostream>
#include <gtest/gtest.h>
class calcFunction
{
public:
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
};
class calcFunctionEnvironment : public testing::Environment
{
public:
virtual void SetUp()
{
val = 123;
std::cout << "--> Environment " << __func__ << " <--" << std::endl;
}
virtual void TearDown()
{
std::cout << "--> Environment " << __func__ << " <--" << std::endl;
}
int val;
};
calcFunctionEnvironment* calc_env;
class calcFunctionTest : public testing::Test
{
protected:
static void SetUpTestCase()
{
std::cout<< "--> " << __func__ << " <--" << std::endl;
}
static void TearDownTestCase()
{
std::cout<< "--> " << __func__ << " <--" << std::endl;
}
virtual void SetUp()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
virtual void TearDown()
{
std::cout << "--> " << __func__ << " <--" <<std::endl;
}
calcFunction calc;
};
TEST_F(calcFunctionTest, test_add)
{
std::cout << "--> test_add start <--" << std::endl;
EXPECT_EQ(calc.add(1,2), 3);
std::cout << "Global Environment val = " << calc_env->val << std::endl;
std::cout << "--> test_add end <--" << std::endl;
}
TEST_F(calcFunctionTest, test_sub)
{
std::cout << "--> test_sub start <--" << std::endl;
EXPECT_EQ(calc.sub(1,2), -1);
std::cout << "Global Environment val = " << calc_env->val << std::endl;
std::cout << "--> test_sub end <--" << std::endl;
}
int main(int argc, char **argv)
{
calc_env = new calcFunctionEnvironment;
testing::AddGlobalTestEnvironment(calc_env);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
如何选择事件:
死亡测试是用来检测测试程序是否按照预期的方式崩溃
#include <iostream>
#include <gtest/gtest.h>
int divison(int a, int b)
{
return a / b;
}
TEST(testCaseDeathTest, test_div)
{
EXPECT_DEATH(divison(1, 0), "");
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
上面这个例子就是死亡测试,其运行结果如下,这里需要注意的是test_case_name如果使用DeathTest为后缀,gTest会优先运行。
首先对 sample1_unittest.cc 进行编译生成 .i 文件
(base) shengtangtang@MacBook-Pro samples % g++ sample1.cc sample1_unittest.cc -E > output.i -lpthread -lgtest -std=c++11 -lgtest_main
在 output.i 中找到一个测试用例的编译过程查看
static_assert(sizeof("IsPrimeTest") > 1, "test_suite_name must not be empty"); static_assert(sizeof("Negative") > 1, "test_name must not be empty");
class IsPrimeTest_Negative_Test : public ::testing::Test
{
public:
IsPrimeTest_Negative_Test() = default;
~IsPrimeTest_Negative_Test() override = default;
IsPrimeTest_Negative_Test(IsPrimeTest_Negative_Test const&) = delete;
IsPrimeTest_Negative_Test& operator=(IsPrimeTest_Negative_Test const&) = delete;
IsPrimeTest_Negative_Test(IsPrimeTest_Negative_Test&&) noexcept = delete;
IsPrimeTest_Negative_Test& operator=(IsPrimeTest_Negative_Test&&) noexcept = delete;
private:
void TestBody() override;
static ::testing::TestInfo* const test_info_ __attribute__ ((unused));
};
::testing::TestInfo* const IsPrimeTest_Negative_Test::test_info_ =
::testing::internal::MakeAndRegisterTestInfo( "IsPrimeTest", "Negative",
nullptr, nullptr,
::testing::internal::CodeLocation("sample1_unittest.cc", 117),
(::testing::internal::GetTestTypeId()),
::testing::internal::SuiteApiResolver< ::testing::Test>::GetSetUpCaseOrSuite("sample1_unittest.cc", 117),
::testing::internal::SuiteApiResolver< ::testing::Test>::GetTearDownCaseOrSuite("sample1_unittest.cc", 117),
new ::testing::internal::TestFactoryImpl<IsPrimeTest_Negative_Test>);
void IsPrimeTest_Negative_Test::TestBody() {
switch (0)
case 0:
default:
if (const ::testing::AssertionResult gtest_ar_ =
::testing::AssertionResult(!(IsPrime(-1)))) ;
else
::testing::internal::AssertHelper(
::testing::TestPartResult::kNonFatalFailure,
"sample1_unittest.cc", 120,
::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "IsPrime(-1)", "true", "false").c_str()
) = ::testing::Message();
switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(IsPrime(-2)))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "sample1_unittest.cc", 121, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "IsPrime(-2)", "true", "false").c_str()) = ::testing::Message();
switch (0) case 0: default: if (const ::testing::AssertionResult gtest_ar_ = ::testing::AssertionResult(!(IsPrime((-2147483647 -1))))) ; else ::testing::internal::AssertHelper(::testing::TestPartResult::kNonFatalFailure, "sample1_unittest.cc", 122, ::testing::internal::GetBoolAssertionFailureMessage( gtest_ar_, "IsPrime((-2147483647 -1))", "true", "false").c_str()) = ::testing::Message();
}
展开后,我们观察到:
test_info_,调用MakeAndRegisterTestInfo对测试案例进行注册gtest.h 中
#if !GTEST_DONT_DEFINE_TEST
#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
#endif
#define GTEST_TEST(test_suite_name, test_name) \
GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
::testing::internal::GetTestTypeId())
#if !GTEST_DONT_DEFINE_TEST
#define TEST_F(test_fixture, test_name)\
GTEST_TEST_(test_fixture, test_name, test_fixture, \
::testing::internal::GetTypeId<test_fixture>())
#endif // !GTEST_DONT_DEFINE_TEST
gtest_internal.h 中
#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \
static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \
"test_suite_name must not be empty"); \
static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \
"test_name must not be empty"); \
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
: public parent_class { \
public: \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \
~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)); \
GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)); \
\
private: \
void TestBody() override; \
static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \
}; \
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::test_info_ = \
::testing::internal::MakeAndRegisterTestInfo( \
#test_suite_name, #test_name, nullptr, nullptr, \
::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \
new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>); \
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
这就是 TEST宏运行时的调用过程
上面关键的方法就是MakeAndRegisterTestInfo了,我们跳到MakeAndRegisterTestInfo函数中:
gtest.cc 中
// Creates a new TestInfo object and registers it with Google Test;
// returns the created object.
//
// Arguments:
//
// test_suite_name: 测试案例的名称
// name: 测试的名称
// type_param: the name of the test's type parameter, or NULL if
// this is not a typed or a type-parameterized test.
// value_param: text representation of the test's value parameter,
// or NULL if this is not a value-parameterized test.
// code_location: code location where the test is defined
// fixture_class_id: test fixture类的ID
// set_up_tc: 事件函数SetUpTestCases的函数地址
// tear_down_tc: 事件函数TearDownTestCases的函数地址
// factory: 工厂对象,用于创建测试对象(Test).
TestInfo* MakeAndRegisterTestInfo(
const char* test_suite_name, const char* name, const char* type_param,
const char* value_param, CodeLocation code_location,
TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
TestInfo* const test_info =
new TestInfo(test_suite_name, name, type_param, value_param,
code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
我们看到,上面创建了一个TestInfo对象,然后通过AddTestInfo注册了这个对象。TestInfo对象到底是一个什么样的东西呢?
TestInfo对象主要用于包含如下信息:
我们还看到,TestInfo的构造函数中,非常重要的一个参数就是工厂对象,它主要负责在运行测试案例时创建出Test对象。我们看到我们上面的例子的factory为:
new ::testing::internal::TestFactoryImpl<IsPrimeTest_Negative_Test>);
我们明白了,Test对象原来就是TEST宏展开后的那个类的对象(FooTest_Demo_Test),再看看TestFactoryImpl的实现:gtest_internal.h
// This class provides implementation of TeastFactoryBase interface.
// It is used in TEST and TEST_F macros.
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
Test* CreateTest() override { return new TestClass; }
};
这个对象工厂够简单吧,嗯,Simple is better。当我们需要创建一个测试对象(Test)时,调用factory的CreateTest()方法就可以了。
创建了TestInfo对象后,再通过下面的方法对TestInfo对象进行注册
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
GetUnitTestImpl()是获取UnitTestImpl对象:gtest_Internal-inl.h
// Convenience function for accessing the global UnitTest
// implementation object.
inline UnitTestImpl* GetUnitTestImpl() {
return UnitTest::GetInstance()->impl();
}
其中UnitTest是一个单件(Singleton),整个进程空间只有一个实例,通过UnitTest::GetInstance()获取单件的实例。上面的代码看到,UnitTestImpl对象是最终是从UnitTest对象中获取的。那么UnitTestImpl到底是一个什么样的东西呢?可以这样理解:
UnitTestImpl是一个在UnitTest内部使用的,为执行单元测试案例而提供了一系列实现的那么一个类。(自己归纳的,可能不准确)
我们上面的AddTestInfo就是其中的一个实现,负责注册TestInfo实例:gtest_Internal-inl.h
// 添加TestInfo对象到整个单元测试中.
//
// 参数::
//
// set_up_tc: 事件函数SetUpTestCases的函数地址
// tear_down_tc: 事件函数TearDownTestCases的函数地址
// test_info: TestInfo对象
void AddTestInfo(internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc,
TestInfo* test_info) {
// 处理死亡测试的代码,先不关注它
#if GTEST_HAS_DEATH_TEST
// In order to support thread-safe death tests, we need to
// remember the original working directory when the test program
// was first invoked. We cannot do this in RUN_ALL_TESTS(), as
// the user may have changed the current directory before calling
// RUN_ALL_TESTS(). Therefore we capture the current directory in
// AddTestInfo(), which is called to register a TEST or TEST_F
// before main() is reached.
if (original_working_dir_.IsEmpty()) {
original_working_dir_.Set(FilePath::GetCurrentDir());
GTEST_CHECK_(!original_working_dir_.IsEmpty())
<< "Failed to get the current working directory.";
}
#endif // GTEST_HAS_DEATH_TEST
// 获取或创建了一个TestCase对象,并将testinfo添加到TestCase对象中。
GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
set_up_tc, tear_down_tc)
->AddTestInfo(test_info);
}
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
TestCase* GetTestCase(const char* test_case_name, const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc) {
return GetTestSuite(test_case_name, type_param, set_up_tc, tear_down_tc);
}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
我们看到,TestCase对象出来了,并通过AddTestInfo添加了一个TestInfo对象。这时,似乎豁然开朗了
// Finds and returns a TestSuite with the given name. If one doesn't
// exist, creates one and returns it. It's the CALLER'S
// RESPONSIBILITY to ensure that this function is only called WHEN THE
// TESTS ARE NOT SHUFFLED.
//
// Arguments:
//
// test_suite_name: name of the test suite
// type_param: the name of the test suite's type parameter, or NULL if
// this is not a typed or a type-parameterized test suite.
// set_up_tc: pointer to the function that sets up the test suite
// tear_down_tc: pointer to the function that tears down the test suite
TestSuite* UnitTestImpl::GetTestSuite(
const char* test_suite_name, const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc) {
// Can we find a TestSuite with the given name?
const auto test_suite =
std::find_if(test_suites_.rbegin(), test_suites_.rend(),
TestSuiteNameIs(test_suite_name));
if (test_suite != test_suites_.rend()) return *test_suite;
// No. Let's create one.
auto* const new_test_suite =
new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc);
// Is this a death test suite?
if (internal::UnitTestOptions::MatchesFilter(test_suite_name,
kDeathTestSuiteFilter)) {
// Yes. Inserts the test suite after the last death test suite
// defined so far. This only works when the test suites haven't
// been shuffled. Otherwise we may end up running a death test
// after a non-death test.
++last_death_test_suite_;
test_suites_.insert(test_suites_.begin() + last_death_test_suite_,
new_test_suite);
} else {
// No. Appends to the end of the list.
test_suites_.push_back(new_test_suite);
}
test_suite_indices_.push_back(static_cast<int>(test_suite_indices_.size()));
return new_test_suite;
}
gtest.h 中
#if !GTEST_DONT_DEFINE_TEST
#define TEST(test_suite_name, test_name) GTEST_TEST(test_suite_name, test_name)
#endif
#define GTEST_TEST(test_suite_name, test_name) \
GTEST_TEST_(test_suite_name, test_name, ::testing::Test, \
::testing::internal::GetTestTypeId())
#if !GTEST_DONT_DEFINE_TEST
#define TEST_F(test_fixture, test_name)\
GTEST_TEST_(test_fixture, test_name, test_fixture, \
::testing::internal::GetTypeId<test_fixture>())
#endif // !GTEST_DONT_DEFINE_TEST
都是使用了GTEST_TEST_宏,在看看这个宏如何定义的, gtest_internal.h 中
#define GTEST_TEST_(test_suite_name, test_name, parent_class, parent_id) \
static_assert(sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1, \
"test_suite_name must not be empty"); \
static_assert(sizeof(GTEST_STRINGIFY_(test_name)) > 1, \
"test_name must not be empty"); \
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
: public parent_class { \
public: \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default; \
~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default; \
GTEST_DISALLOW_COPY_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)); \
GTEST_DISALLOW_MOVE_AND_ASSIGN_(GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)); \
\
private: \
void TestBody() override; \
static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_; \
}; \
\
::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::test_info_ = \
::testing::internal::MakeAndRegisterTestInfo( \
#test_suite_name, #test_name, nullptr, nullptr, \
::testing::internal::CodeLocation(__FILE__, __LINE__), (parent_id), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__), \
::testing::internal::SuiteApiResolver< \
parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__), \
new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>); \
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
GTEST_TEST_宏不仅要求传入测试用例和测试实例名,还要传入Test类名和其ID。我们将GTEST_TEST_的实现拆成三段分析:
首先使用宏GTEST_TEST_CLASS_NAME_生成类名。该类暴露了一个空的默认构造函数、一个私有的虚函数TestBody、一个静态变量test_info_和一个私有的赋值运算符(将运算符=私有化,限制类对象的赋值和拷贝行为)
不需要多解释了,和我们上面展开看到的差不多,不过这里比较明确的看到了,我们在TEST宏里写的就是TestBody里的东西。这里再补充说明一下里面的GTEST_DISALLOW_COPY_AND_ASSIGN_宏,我们上面的例子看出,这个宏展开后:
IsPrimeTest_Negative_Test(IsPrimeTest_Negative_Test const&) = delete;
IsPrimeTest_Negative_Test& operator=(IsPrimeTest_Negative_Test const&) = delete;
正如这个宏的名字一样,它是用于防止对对象进行拷贝和赋值操作的。
静态变量test_info的作用非常有意思,它利用”静态变量在程序运行前被初始化“的特性,抢在main函数执行之前,执行一段代码,从而有机会将测试用例放置于一个固定的位置。这个是”自动“保存测试用例的本质所在。
最后一行:void GTEST_TEST_CLASS_NAME_(test_case_name,test_name)::TestBody()
这行要在类外提供TestBody函数的实现。我们要注意下,这个只是函数的一部分,即它只是包含了函数返回类型、函数名,而真正的函数实体是在TEST宏之后的{}内的,如
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0));
}
这段代码最后应该如下,它实际上是测试逻辑的主体。
……
void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() {
EXPECT_EQ(1, Factorial(0));
}
可以说TEST宏的写法只是一种类函数的写法,而实际它“偷梁换柱”,实现了测试的实体
我们再看下test_info_的初始化逻辑,它调用了::testing::internal::MakeAndRegisterTestInfo函数
::testing::internal::都是命名空间
我们先关注下最后一个参数,它是一个模板类,模板是当前类名。同时从名字上看,它也是一个工厂类。该类继承于TestFactoryBase,并重载了CreateTest方法——它只是new出了一个模板类对象,并返回
/* gtese-internal.h */
template <class TestClass>
class TestFactoryImpl : public TestFactoryBase {
public:
virtual Test* CreateTest() { return new TestClass; }
};
MakeAndRegisterTestInfo函数的实现也非常简单:它 new出一个 TestInfo类对象,并调用UnitTestImpl单例的AddTestInfo方法,将其保存起来。
TestInfo* MakeAndRegisterTestInfo(
const char* test_suite_name, const char* name, const char* type_param,
const char* value_param, CodeLocation code_location,
TypeId fixture_class_id, SetUpTestSuiteFunc set_up_tc,
TearDownTestSuiteFunc tear_down_tc, TestFactoryBase* factory) {
TestInfo* const test_info =
new TestInfo(test_suite_name, name, type_param, value_param,
code_location, fixture_class_id, factory);
GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info);
return test_info;
}
AddTestInfo试图通过测试用例名等信息获取测试用例,然后调用测试用例对象去新增一个测试特例——test_info。这样我们在此就将测试用例和测试特例的关系在代码中找到了关联。
/* AddTestInfo函数中,gtest-internal-inl.h */
GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
set_up_tc, tear_down_tc)
->AddTestInfo(test_info);
但是如果第一次调用TEST宏,是不会有测试用例类的,那么其中新建测试用例对象,并保存到UnitTestImpl类单例对象的test_cases_中的逻辑是在GetTestCase函数实现中
TestSuite* UnitTestImpl::GetTestSuite(
const char* test_suite_name, const char* type_param,
internal::SetUpTestSuiteFunc set_up_tc,
internal::TearDownTestSuiteFunc tear_down_tc) {
// Can we find a TestSuite with the given name?
const auto test_suite =
std::find_if(test_suites_.rbegin(), test_suites_.rend(),
TestSuiteNameIs(test_suite_name));
if (test_suite != test_suites_.rend()) return *test_suite;
// No. Let's create one.
auto* const new_test_suite =
new TestSuite(test_suite_name, type_param, set_up_tc, tear_down_tc);
// Is this a death test suite?
if (internal::UnitTestOptions::MatchesFilter(test_suite_name,
kDeathTestSuiteFilter)) {
// Yes. Inserts the test suite after the last death test suite
// defined so far. This only works when the test suites haven't
// been shuffled. Otherwise we may end up running a death test
// after a non-death test.
++last_death_test_suite_;
test_suites_.insert(test_suites_.begin() + last_death_test_suite_,
new_test_suite);
} else {
// No. Appends to the end of the list.
test_suites_.push_back(new_test_suite);
}
test_suite_indices_.push_back(static_cast<int>(test_suite_indices_.size()));
return new_test_suite;
}
正如我们所料,在没有找到测试实例对象指针的情况下,新建了一个TestCase测试用例对象,并将其指针保存到了test_cases_(test_suites_)中。如此我们就解释了,测试用例是如何被保存的了
接着上例的分析,如下代码将测试特例信息通过TestCase类的AddTestInfo方法保存起来
/* AddTestInfo函数中,gtest-internal-inl.h */
GetTestSuite(test_info->test_suite_name(), test_info->type_param(),
set_up_tc, tear_down_tc)
->AddTestInfo(test_info);
void TestSuite::AddTestInfo(TestInfo* test_info) {
test_info_list_.push_back(test_info);
test_indices_.push_back(static_cast<int>(test_indices_.size()));
}
可见test_info_list_中保存了测试特例信息
在之前的测试代码中,我们并没有发现main函数。但是C/C++语言要求程序必须要有程序入口,那Main函数呢?其实GTest为了让我们可以更简单的使用它,为我们编写了一个main函数,它位于src目录下gtest_main.cc文件中
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from gtest_main.cc\n");
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Makefile文件编译了该文件,并将其链接到可执行文件中。这样我们的程序就有了入口。那么这个main函数又是如何将执行流程引到我们的代码中的呢?代码之前了无秘密。短短的这几行,只有04行才可能是我们的代码入口。(03行将程序入参传递给了Gtest库,从而实现了《Google Test(GTest)使用方法和源码解析——概况》中所述的“选择性测试”)。很显然,它的名字——RUN_ALL_TESTS也暴露了它的功能。我们来看下其实现
一。
我们的测试案例的运行就是通过这个宏发起的。RUN_ALL_TEST的定义非常简单:
int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_;
inline int RUN_ALL_TESTS() {
return ::testing::UnitTest::GetInstance()->Run();
}
二。
我们又看到了熟悉的::testing::UnitTest::GetInstance(),看来案例的执行时从UnitTest的Run方法开始的,最终调用了UnitTest类的单例(GetInstance)的Run方法,最后的 return 为 Run方法的核心实现, gtest.cc
int UnitTest::Run() {
const bool in_death_test_child_process =
internal::GTEST_FLAG(internal_run_death_test).length() > 0;
// Google Test implements this protocol for catching that a test
// program exits before returning control to Google Test:
//
// 1. Upon start, Google Test creates a file whose absolute path
// is specified by the environment variable
// TEST_PREMATURE_EXIT_FILE.
// 2. When Google Test has finished its work, it deletes the file.
//
// This allows a test runner to set TEST_PREMATURE_EXIT_FILE before
// running a Google-Test-based test program and check the existence
// of the file at the end of the test execution to see if it has
// exited prematurely.
// If we are in the child process of a death test, don't
// create/delete the premature exit file, as doing so is unnecessary
// and will confuse the parent process. Otherwise, create/delete
// the file upon entering/leaving this function. If the program
// somehow exits before this function has a chance to return, the
// premature-exit file will be left undeleted, causing a test runner
// that understands the premature-exit-file protocol to report the
// test as having failed.
const internal::ScopedPrematureExitFile premature_exit_file(
in_death_test_child_process
? nullptr
: internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE"));
// Captures the value of GTEST_FLAG(catch_exceptions). This value will be
// used for the duration of the program.
impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions));
#if GTEST_OS_WINDOWS
// Either the user wants Google Test to catch exceptions thrown by the
// tests or this is executing in the context of death test child
// process. In either case the user does not want to see pop-up dialogs
// about crashes - they are expected.
if (impl()->catch_exceptions() || in_death_test_child_process) {
# if !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_WINDOWS_PHONE && !GTEST_OS_WINDOWS_RT
// SetErrorMode doesn't exist on CE.
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT |
SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX);
# endif // !GTEST_OS_WINDOWS_MOBILE
# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE
// Death test children can be terminated with _abort(). On Windows,
// _abort() can show a dialog with a warning message. This forces the
// abort message to go to stderr instead.
_set_error_mode(_OUT_TO_STDERR);
# endif
# if defined(_MSC_VER) && !GTEST_OS_WINDOWS_MOBILE
// In the debug version, Visual Studio pops up a separate dialog
// offering a choice to debug the aborted program. We need to suppress
// this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement
// executed. Google Test will notify the user of any unexpected
// failure via stderr.
if (!GTEST_FLAG(break_on_failure))
_set_abort_behavior(
0x0, // Clear the following flags:
_WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump.
// In debug mode, the Windows CRT can crash with an assertion over invalid
// input (e.g. passing an invalid file descriptor). The default handling
// for these assertions is to pop up a dialog and wait for user input.
// Instead ask the CRT to dump such assertions to stderr non-interactively.
if (!IsDebuggerPresent()) {
(void)_CrtSetReportMode(_CRT_ASSERT,
_CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
(void)_CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
}
# endif
}
#endif // GTEST_OS_WINDOWS
return internal::HandleExceptionsInMethodIfSupported(
impl(),
&internal::UnitTestImpl::RunAllTests,
"auxiliary test code (environments or event listeners)") ? 0 : 1;
}
我们又看到了熟悉的impl(UnitTestImpl),impl()方法返回了一个UnitTestImpl对象指针impl_,它是在UniTes类的构造函数中生成的(HandleExceptionsInMethodIfSupported函数见《Google Test(GTest)使用方法和源码解析——概况》分析,主要处理兼容性问题),
// Creates an empty UnitTest.
UnitTest::UnitTest() {
impl_ = new internal::UnitTestImpl(this);
}
三。
具体案例该怎么执行,还是得靠UnitTestImpl。 gtest.cc
bool UnitTestImpl::RunAllTests() {
// True if and only if Google Test is initialized before RUN_ALL_TESTS() is
// called.
const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized();
// Do not run any test if the --help flag was specified.
if (g_help_flag)
return true;
// Repeats the call to the post-flag parsing initialization in case the
// user didn't call InitGoogleTest.
PostFlagParsingInit();
// Even if sharding is not on, test runners may want to use the
// GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding
// protocol.
internal::WriteToShardStatusFileIfNeeded();
// True if and only if we are in a subprocess for running a thread-safe-style
// death test.
bool in_subprocess_for_death_test = false;
#if GTEST_HAS_DEATH_TEST
in_subprocess_for_death_test =
(internal_run_death_test_flag_.get() != nullptr);
# if defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
if (in_subprocess_for_death_test) {
GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_();
}
# endif // defined(GTEST_EXTRA_DEATH_TEST_CHILD_SETUP_)
#endif // GTEST_HAS_DEATH_TEST
const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex,
in_subprocess_for_death_test);
// Compares the full test names with the filter to decide which
// tests to run.
const bool has_tests_to_run = FilterTests(should_shard
? HONOR_SHARDING_PROTOCOL
: IGNORE_SHARDING_PROTOCOL) > 0;
// Lists the tests and exits if the --gtest_list_tests flag was specified.
if (GTEST_FLAG(list_tests)) {
// This must be called *after* FilterTests() has been called.
ListTestsMatchingFilter();
return true;
}
random_seed_ = GTEST_FLAG(shuffle) ?
GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0;
// True if and only if at least one test has failed.
bool failed = false;
TestEventListener* repeater = listeners()->repeater();
start_timestamp_ = GetTimeInMillis();
repeater->OnTestProgramStart(*parent_);
// How many times to repeat the tests? We don't want to repeat them
// when we are inside the subprocess of a death test.
const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat);
// Repeats forever if the repeat count is negative.
const bool gtest_repeat_forever = repeat < 0;
for (int i = 0; gtest_repeat_forever || i != repeat; i++) {
// We want to preserve failures generated by ad-hoc test
// assertions executed before RUN_ALL_TESTS().
ClearNonAdHocTestResult();
Timer timer;
// Shuffles test suites and tests if requested.
if (has_tests_to_run && GTEST_FLAG(shuffle)) {
random()->Reseed(static_cast<uint32_t>(random_seed_));
// This should be done before calling OnTestIterationStart(),
// such that a test event listener can see the actual test order
// in the event.
ShuffleTests();
}
// Tells the unit test event listeners that the tests are about to start.
repeater->OnTestIterationStart(*parent_, i);
// Runs each test suite if there is at least one test to run.
if (has_tests_to_run) {
// Sets up all environments beforehand. 执行全局的 SetUp事件
repeater->OnEnvironmentsSetUpStart(*parent_);
ForEach(environments_, SetUpEnvironment);
repeater->OnEnvironmentsSetUpEnd(*parent_);
// Runs the tests only if there was no fatal failure or skip triggered
// during global set-up.
if (Test::IsSkipped()) {
// Emit diagnostics when global set-up calls skip, as it will not be
// emitted by default.
TestResult& test_result =
*internal::GetUnitTestImpl()->current_test_result();
for (int j = 0; j < test_result.total_part_count(); ++j) {
const TestPartResult& test_part_result =
test_result.GetTestPartResult(j);
if (test_part_result.type() == TestPartResult::kSkip) {
const std::string& result = test_part_result.message();
printf("%s\n", result.c_str());
}
}
fflush(stdout);
} else if (!Test::HasFatalFailure()) { //如果全局事件执行成功的话,执行每个用例
for (int test_index = 0; test_index < total_test_suite_count();
test_index++) {
GetMutableSuiteCase(test_index)->Run();
if (GTEST_FLAG(fail_fast) &&
GetMutableSuiteCase(test_index)->Failed()) {
for (int j = test_index + 1; j < total_test_suite_count(); j++) {
GetMutableSuiteCase(j)->Skip();
}
break;
}
}
} else if (Test::HasFatalFailure()) {
// If there was a fatal failure during the global setup then we know we
// aren't going to run any tests. Explicitly mark all of the tests as
// skipped to make this obvious in the output.
for (int test_index = 0; test_index < total_test_suite_count();
test_index++) {
GetMutableSuiteCase(test_index)->Skip();
}
}
// Tears down all environments in reverse order afterwards. 执行全局的TearDown事件
repeater->OnEnvironmentsTearDownStart(*parent_);
std::for_each(environments_.rbegin(), environments_.rend(),
TearDownEnvironment);
repeater->OnEnvironmentsTearDownEnd(*parent_);
}
elapsed_time_ = timer.Elapsed();
// Tells the unit test event listener that the tests have just finished.
repeater->OnTestIterationEnd(*parent_, i); //执行完成
// Gets the result and clears it.
if (!Passed()) {
failed = true;
}
// Restores the original test order after the iteration. This
// allows the user to quickly repro a failure that happens in the
// N-th iteration without repeating the first (N - 1) iterations.
// This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in
// case the user somehow changes the value of the flag somewhere
// (it's always safe to unshuffle the tests).
UnshuffleTests();
if (GTEST_FLAG(shuffle)) {
// Picks a new random seed for each iteration.
random_seed_ = GetNextRandomSeed(random_seed_);
}
}
repeater->OnTestProgramEnd(*parent_);
if (!gtest_is_initialized_before_run_all_tests) {
ColoredPrintf(
GTestColor::kRed,
"\nIMPORTANT NOTICE - DO NOT IGNORE:\n"
"This test program did NOT call " GTEST_INIT_GOOGLE_TEST_NAME_
"() before calling RUN_ALL_TESTS(). This is INVALID. Soon " GTEST_NAME_
" will start to enforce the valid usage. "
"Please fix it ASAP, or IT WILL START TO FAIL.\n"); // NOLINT
#if GTEST_FOR_GOOGLE_
ColoredPrintf(GTestColor::kRed,
"For more details, see http://wiki/Main/ValidGUnitMain.\n");
#endif // GTEST_FOR_GOOGLE_
}
return !failed;
}
核心代码为:
for (int test_index = 0; test_index < total_test_suite_count(); test_index++) {
GetMutableSuiteCase(test_index)->Run();
上面,我们很开心的看到了我们前面讲到的全局事件的调用。environments_是一个Environment的vector结构,它的内容是我们在main中通过:
// The vector of environments that need to be set-up/torn-down
// before/after the tests are run.
// **gtest-internal-inl.h**
std::vector<Environment*> environments_;
// gtest.h
class Environment {
public:
// The d'tor is virtual as we need to subclass Environment.
virtual ~Environment() {}
// Override this to define how to set up the environment.
virtual void SetUp() {}
// Override this to define how to tear down the environment.
virtual void TearDown() {}
private:
// If you see an error about overriding the following function or
// about it being private, you have mis-spelled SetUp() as Setup().
struct Setup_should_be_spelled_SetUp {};
virtual Setup_should_be_spelled_SetUp* Setup() { return nullptr; }
};
testing::AddGlobalTestEnvironment(new FooEnvironment);
添加进去的。test_cases_我们之前也了解过了,是一个TestCase的链表结构(List)。gtest实现了一个链表,并且提供了一个Foreach方法,迭代调用某个函数,并将里面的元素作为函数的参数: gtest-internal-inl.h
// Applies a function/functor to each element in the container.
template <class Container, typename Functor>
void ForEach(const Container& c, Functor functor) {
std::for_each(c.begin(), c.end(), functor);
}
因此,我们关注一下:repeater->OnEnvironmentsSetUpStart(*parent_);,其实是迭代调用了OnEnvironmentsSetUpStart函数, gtest.cc
void OnEnvironmentsSetUpStart(const UnitTest& unit_test) override;
void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart(
const UnitTest& /*unit_test*/) {
ColoredPrintf(GTestColor::kGreen, "[----------] ");
printf("Global test environment set-up.\n");
fflush(stdout);
}
四。
再看看GetMutableSuiteCase(test_index)->Run()的实现,其逐个返回UnitTestImpl对象成员变量test_cases_中的元素——各个测试用例对象指针,然后调用测试用例的Run方法, gtest.cc
// Runs every test in this TestSuite.
void TestSuite::Run() {
if (!should_run_) return;
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
impl->set_current_test_suite(this);
TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
// Call both legacy and the new API
repeater->OnTestSuiteStart(*this);
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
repeater->OnTestCaseStart(*this);
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// SetUpTestCases事件在这里调用
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
this, &TestSuite::RunSetUpTestSuite, "SetUpTestSuite()");
start_timestamp_ = internal::GetTimeInMillis();
internal::Timer timer;
// 前面分析的一个TestCase对应多个TestInfo,因此,在这里迭代对TestInfo调用RunTest方法
for (int i = 0; i < total_test_count(); i++) {
GetMutableTestInfo(i)->Run();
if (GTEST_FLAG(fail_fast) && GetMutableTestInfo(i)->result()->Failed()) {
for (int j = i + 1; j < total_test_count(); j++) {
GetMutableTestInfo(j)->Skip();
}
break;
}
}
elapsed_time_ = timer.Elapsed();
// TearDownTestCases事件在这里调用
impl->os_stack_trace_getter()->UponLeavingGTest();
internal::HandleExceptionsInMethodIfSupported(
this, &TestSuite::RunTearDownTestSuite, "TearDownTestSuite()");
// Call both legacy and the new API
repeater->OnTestSuiteEnd(*this);
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
repeater->OnTestCaseEnd(*this);
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
impl->set_current_test_suite(nullptr);
}
核心代码为;
for (int i = 0; i < total_test_count(); i++) {
GetMutableTestInfo(i)->Run();
第二种事件机制又浮出我们眼前,非常兴奋。可以看出,SetUpTestCases和TearDownTestCaess是在一个TestCase之前和之后调用的。
五。
接着看GetMutableTestInfo(i)->Run();
和UnitTest很类似,TestInfo内部也有一个主管各种实现的类, gtest.cc
TestInfo::TestInfo(const std::string& a_test_suite_name,
const std::string& a_name, const char* a_type_param,
const char* a_value_param,
internal::CodeLocation a_code_location,
internal::TypeId fixture_class_id,
internal::TestFactoryBase* factory)
: test_suite_name_(a_test_suite_name),
name_(a_name),
type_param_(a_type_param ? new std::string(a_type_param) : nullptr),
value_param_(a_value_param ? new std::string(a_value_param) : nullptr),
location_(a_code_location),
fixture_class_id_(fixture_class_id),
should_run_(false),
is_disabled_(false),
matches_filter_(false),
is_in_another_shard_(false),
factory_(factory),
result_() {}
六。
因此,案例的执行还得看TestInfo的Run()方法, gtest.cc
void TestInfo::Run() {
if (!should_run_) return;
// Tells UnitTest where to store test result.
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
impl->set_current_test_info(this);
TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater();
// Notifies the unit test event listeners that a test is about to start.
repeater->OnTestStart(*this);
// 开始计时
result_.set_start_timestamp(internal::GetTimeInMillis());
internal::Timer timer;
impl->os_stack_trace_getter()->UponLeavingGTest();
// Creates the test object. 我们的对象工厂,使用CreateTest()生成Test对象
Test* const test = internal::HandleExceptionsInMethodIfSupported(
factory_, &internal::TestFactoryBase::CreateTest,
"the test fixture's constructor");
// Runs the test if the constructor didn't generate a fatal failure or invoke
// GTEST_SKIP().
// Note that the object will not be null, // 如果Test对象创建成功
if (!Test::HasFatalFailure() && !Test::IsSkipped()) {
// This doesn't throw as all user code that can throw are wrapped into
// exception handling code.
test->Run(); // // 调用Test对象的Run()方法,执行测试案例
}
if (test != nullptr) {
// Deletes the test object.
impl->os_stack_trace_getter()->UponLeavingGTest(); // 执行完毕,删除Test对象
internal::HandleExceptionsInMethodIfSupported(
test, &Test::DeleteSelf_, "the test fixture's destructor");
}
// // 停止计时
result_.set_elapsed_time(timer.Elapsed());
// Notifies the unit test event listener that a test has just finished.
repeater->OnTestEnd(*this);
// Tells UnitTest to stop associating assertion results to this
// test.
impl->set_current_test_info(nullptr);
}
测试特例的Run方法其核心是
Test* const test = internal::HandleExceptionsInMethodIfSupported(
factory_, &internal::TestFactoryBase::CreateTest,
"the test fixture's constructor");
// Runs the test if the constructor didn't generate a fatal failure or invoke
// GTEST_SKIP().
// Note that the object will not be null, // 如果Test对象创建成功
if (!Test::HasFatalFailure() && !Test::IsSkipped()) {
// This doesn't throw as all user code that can throw are wrapped into
// exception handling code.
test->Run(); // // 调用Test对象的Run()方法,执行测试案例
}
它通过构造函数传入的工厂类对象指针调用其重载的CreateTest方法,new出TEST宏中定义的使用GTEST_TEST_CLASS_NAME_命名(用例名_实例名_TEST)的类(之后称测试用例特例类)的对象指针,然后调用测试用例特例类的父类中的Run方法。由于测试用例特例类继承::testing::Test类后,并没有重载其Run方法,所以其调用的还是Test类的Run方法,而Test类的Run方法实际上只是调用了测试用例特例类重载了的TestBody方法
internal::HandleExceptionsInMethodIfSupported(this, &Test::TestBody, "the test body");
而TestBody就是我们之前在分析TEST宏时讲解通过“偷梁换柱”实现的虚方法。
七。
上面看到了我们前面讲到的对象工厂fatory,通过fatory的CreateTest()方法,创建Test对象,然后执行案例又是通过Test对象的Run()方法:
// Runs the test and updates the test result.
void Test::Run() {
if (!HasSameFixtureClass()) return;
internal::UnitTestImpl* const impl = internal::GetUnitTestImpl();
impl->os_stack_trace_getter()->UponLeavingGTest();
// 每个案例的SetUp事件在这里调用
internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()");
// We will run the test only if SetUp() was successful and didn't call
// GTEST_SKIP().
if (!HasFatalFailure() && !IsSkipped()) {
impl->os_stack_trace_getter()->UponLeavingGTest();
// 我们定义在TEST宏里的东西终于被调用了!
internal::HandleExceptionsInMethodIfSupported(
this, &Test::TestBody, "the test body");
}
// However, we want to clean up as much as possible. Hence we will
// always call TearDown(), even if SetUp() or the test body has
// failed.
impl->os_stack_trace_getter()->UponLeavingGTest();
// 每个案例的TearDown事件在这里调用
internal::HandleExceptionsInMethodIfSupported(
this, &Test::TearDown, "TearDown()");
}
本文通过分析TEST宏和RUN_ALL_TEST宏,了解到了整个gtest运作过程:
gtest.h -> gtest.cc
主函数运行 RUN_ALL_TESTS() /* return ::testing::UnitTest::GetInstance()->Run(); */
-> UnitTest::Run() /* return internal::HandleExceptionsInMethodIfSupported(impl(), &internal::UnitTestImpl::RunAllTests, "auxiliary test code (environments or event listeners)") ? 0 : 1; */
-> UnitTestImpl::RunAllTests() /* GetMutableSuiteCase(test_index)->Run(); */
-> TestSuite::Run() /* GetMutableTestInfo(i)->Run(); */
-> TestInfo::Run() /* test->Run(); // 调用Test对象的Run()方法,执行测试案例 */
-> Test::Run() /* internal::HandleExceptionsInMethodIfSupported(this, &Test::TestBody, "the test body"); */
总结一下gtest里的几个关键的对象:
class GTEST_API_ UnitTest。class GTEST_API_ UnitTestImplusing TestCase = TestSuite;, class GTEST_API_ TestSuiteclass GTEST_API_ TestInfo, gtest.h 中定义